<!DOCTYPE html>

<!-- script标签会把请求过来的响应数据当做JS脚本自动执行 -->
<!-- 已登录，返回：var isLogin = true -->
<!-- 未登录，返回：var isLogin = false -->
<script src="/login/status"></script>
<script>
  // console.log(isLogin, 'isLogin');
  if (!isLogin) {
    location.href = '/admin/login.html'
  }
</script>

<html lang="zh-CN">

<head>
  <meta charset="utf-8">
  <title>Users &laquo; Admin</title>
  <link rel="stylesheet" href="/assets/vendors/bootstrap/css/bootstrap.css">
  <link rel="stylesheet" href="/assets/vendors/font-awesome/css/font-awesome.css">
  <link rel="stylesheet" href="/assets/vendors/nprogress/nprogress.css">
  <link rel="stylesheet" href="/assets/css/admin.css">
  <script src="/assets/vendors/nprogress/nprogress.js"></script>
</head>

<body>
  <script>NProgress.start()</script>

  <!-- 右侧主体内容 -->
  <div class="main">
    <nav class="navbar">
      <button class="btn btn-default navbar-btn fa fa-bars"></button>
      <ul class="nav navbar-nav navbar-right">
        <li><a href="profile.html"><i class="fa fa-user"></i>个人中心</a></li>
        <li><a href="javascript:;" id="logoutBtn"><i class="fa fa-sign-out"></i>退出</a></li>
      </ul>
    </nav>
    <div class="container-fluid">
      <div class="page-title">
        <h1>用户</h1>
      </div>
      <!-- 有错误信息时展示 -->
      <!-- <div class="alert alert-danger">
        <strong>错误！</strong>发生XXX错误
      </div> -->
      <div class="row">
        <!-- 左侧表单区域 -->
        <div class="col-md-4" id="formBox"></div>
        <!-- /左侧表单区域 -->
        <!-- 右侧列表区域 -->
        <div class="col-md-8">
          <div class="page-action">
            <!-- show when multiple checked -->
            <a id="batchDelBtn" class="btn btn-danger btn-sm" href="javascript:;" disabled>批量删除</a>
          </div>
          <table class="table table-striped table-bordered table-hover">
            <thead>
              <tr>
                <th class="text-center" width="40">
                  <input type="checkbox" id="checkAll">
                </th>
                <th class="text-center" width="80">头像</th>
                <th>邮箱</th>
                <th>昵称</th>
                <th>状态</th>
                <th>角色</th>
                <th class="text-center" width="100">操作</th>
              </tr>
            </thead>
            <!-- 在tbody中显示用户列表，一个用户就是一个tr -->
            <tbody id="userListBox"></tbody>
          </table>
        </div>
        <!-- /右侧列表区域 -->
      </div>
    </div>
  </div>
  <!-- /右侧主体内容 -->

  <!-- 左侧菜单区域 -->
  <div class="aside">
    <div class="profile">
      <img class="avatar" src="/uploads/avatar.jpg">
      <h3 class="name">Coder</h3>
    </div>
    <ul class="nav">
      <li>
        <a href="index.html"><i class="fa fa-dashboard"></i>仪表盘</a>
      </li>
      <li>
        <a href="#menu-posts" class="collapsed" data-toggle="collapse">
          <i class="fa fa-thumb-tack"></i>文章<i class="fa fa-angle-right"></i>
        </a>
        <ul id="menu-posts" class="collapse">
          <li><a href="posts.html">所有文章</a></li>
          <li><a href="post-add.html">写文章</a></li>
          <li><a href="categories.html">分类目录</a></li>
        </ul>
      </li>
      <li>
        <a href="comments.html"><i class="fa fa-comments"></i>评论</a>
      </li>
      <li class="active">
        <a href="users.html"><i class="fa fa-users"></i>用户</a>
      </li>
      <li>
        <a href="#menu-settings" class="collapsed" data-toggle="collapse">
          <i class="fa fa-cogs"></i>设置<i class="fa fa-angle-right"></i>
        </a>
        <ul id="menu-settings" class="collapse">
          <li><a href="slides.html">图片轮播</a></li>
          <li><a href="settings.html">网站设置</a></li>
        </ul>
      </li>
    </ul>
  </div>
  <!-- /左侧菜单区域 -->

  <script src="/assets/vendors/jquery/jquery.js"></script>
  <script src="/assets/vendors/bootstrap/js/bootstrap.js"></script>
  <script>NProgress.done()</script>
  <script src="/assets/js/logout.js"></script>
  <script src="/assets/vendors/art-template/template-web.js"></script>

  <!-- 用户列表模板 -->
  <script type="text/html" id="userListTpl">
    {{ each data v }}
    <tr>
      <td class="text-center"><input type="checkbox" data-id="{{ v._id }}"></td>
      <!-- <td class="text-center"><img class="avatar" src="{{ v.avatar ? v.avatar : '/assets/img/default.png' }}"></td> -->
      <td class="text-center"><img class="avatar" src="{{ v.avatar || '/assets/img/default.png' }}"></td>
      <td>{{ v.email }}</td>
      <td>{{ v.nickName }}</td>
      <td>{{ ['未激活', '已激活'][v.status] }}</td>
      <td>{{ { 'normal': '普通用户', 'admin': '超级管理员' }[v.role] }}</td>
      <td class="text-center">
        <a href="javascript:;" class="btn btn-default btn-xs edit" data-id="{{ v._id }}">编辑</a>
        <a href="javascript:;" class="btn btn-danger btn-xs del" data-id="{{ v._id }}">删除</a>
      </td>
    </tr>
    {{ /each }}
  </script>

  <!-- 添加用户表单模板 -->
  <script type="text/html" id="addUserFormTpl">
  <!-- 套一层表单仅仅只是为了让我们在发送ajax请求时获取表单数据更方便，并不会直接采用传统的表单提交 -->
  <form id="addUserForm">
    <h2>添加新用户</h2>
    <div class="form-group">
      <label>头像</label>
      <div class="form-group">
        <label class="form-image">
          <input id="touxiang" type="file">
          <img id="previewImg" src="/assets/img/default.png">
          <i class="mask fa fa-upload"></i>
          <!-- type=hidden 是隐藏域 用户在浏览器中是看不到这个输入框的，但是在提交表单时，会随着表单数据一起提交 -->
          <input id="hiddenAvatar" type="hidden" name="avatar">
        </label>
      </div>
    </div>
    <div class="form-group">
      <label>邮箱</label>
      <input class="form-control" type="email" placeholder="请输入邮箱" name="email">
    </div>
    <div class="form-group">
      <label>昵称</label>
      <input class="form-control" type="text" placeholder="请输入昵称" name="nickName">
    </div>
    <div class="form-group">
      <label>密码</label>
      <input class="form-control" type="text" placeholder="请输入密码" name="password">
    </div>
    <div class="form-group">
      <div class="radio-inline">
        <label><input type="radio" name="status" value="0">未激活</label>
      </div>
      <div class="radio-inline">
        <label><input type="radio" name="status" value="1">激活</label>
      </div>
    </div>
    <div class="form-group">
      <div class="radio-inline">
        <label><input type="radio" name="role" value="admin">超级管理员</label>
      </div>
      <div class="radio-inline">
        <label><input type="radio" name="role" value="normal">普通用户</label>
      </div>
    </div>
    <div class="form-group">
      <input type="submit" class="btn btn-primary" value="提交">
      <input type="reset" class="btn btn-danger" value="取消">
    </div>
  </form>
  </script>

  <!-- 编辑用户表单模板 -->
  <script type="text/html" id="editUserFormTpl">
  <!-- 套一层表单仅仅只是为了让我们在发送ajax请求时获取表单数据更方便，并不会直接采用传统的表单提交 -->
  <form id="editUserForm" data-id="{{ _id }}">
    <h2>编辑用户（{{ email }}）</h2>
    <div class="form-group">
      <label>头像</label>
      <div class="form-group">
        <label class="form-image">
          <input id="touxiang" type="file">
          <img id="previewImg" src="{{ avatar || '/assets/img/default.png' }}">
          <i class="mask fa fa-upload"></i>
          <!-- type=hidden 是隐藏域 用户在浏览器中是看不到这个输入框的，但是在提交表单时，会随着表单数据一起提交 -->
          <input id="hiddenAvatar" type="hidden" name="avatar" value="{{ avatar || '' }}">
        </label>
      </div>
    </div>
    <div class="form-group">
      <label>邮箱</label>
      <input class="form-control" type="email" placeholder="请输入邮箱" value="{{ email }}" disabled>
    </div>
    <div class="form-group">
      <label>昵称</label>
      <input class="form-control" type="text" placeholder="请输入昵称" name="nickName" value="{{ nickName }}">
    </div>
    <div class="form-group">
      <div class="radio-inline">
        <label><input type="radio" name="status" value="0" {{ status === 0 ? 'checked' : '' }}>未激活</label>
      </div>
      <div class="radio-inline">
        <label><input type="radio" name="status" value="1" {{ status === 1 ? 'checked' : '' }}>激活</label>
      </div>
    </div>
    <div class="form-group">
      <div class="radio-inline">
        <label><input type="radio" name="role" value="admin" {{ role === 'admin' ? 'checked' : '' }}>超级管理员</label>
      </div>
      <div class="radio-inline">
        <label><input type="radio" name="role" value="normal" {{ role === 'normal' ? 'checked' : '' }}>普通用户</label>
      </div>
    </div>
    <div class="form-group">
      <input type="submit" class="btn btn-primary" value="提交">
      <input type="button" class="btn btn-default reset" value="重置" data-id="{{ _id }}">
      <input type="button" class="btn btn-danger cancel" value="取消">
    </div>
  </form>
  </script>

  <script>
    // jq的入口函数，会等页面加载完成后再执行
    $(() => {
      const $userListBox = $('#userListBox')
      const $formBox = $('#formBox')
      const $checkAll = $('#checkAll')
      const $batchDelBtn = $('#batchDelBtn')

      // 渲染添加用户表单
      const renderAddUserForm = () => {
        $formBox.html(template('addUserFormTpl'))
      }

      // 页面第一次加载的时候就要渲染添加用户表单
      renderAddUserForm()

      // 渲染编辑用户表单
      const renderEditUserForm = (user) => {
        // { _id: 'asdf', nickName: '', email: '', ... }
        // 这里直接把用户对象作为模板数据，在模板中，就可以直接用输出语法拿到用户对象的属性值，如：{{ _id }}、{{ email }}
        $formBox.html(template('editUserFormTpl', user))
      }

      // 这个数组用来存放用户列表数据
      let userAry = []

      // 渲染用户列表
      const renderUserList = () => {
        $userListBox.html(template('userListTpl', { data: userAry }))
      }

      // 页面一加载的时候就去请求用户列表数据
      $.ajax({
        type: 'GET',
        url: '/users',
        success(res) {
          userAry = res
          // userAry.push(...res)
          renderUserList()
        }
      })

      // 学过的知识点，要抽时间复习，不要学完就不管了，当时你可能听得懂，但是时间长了，肯定就记忆模糊了
      // 不要觉得你已经搞懂了，就不去复习了，搞懂了去复习时，复习得会快一些，过一遍即可
      // 学习要时刻保持怀疑的心态，要有自己的理解，然后总结

      // 调试BUG时，如果短时间找不出问题所在，那么就先回退到没有BUG的位置，然后再逐步进行调试

      // 注意：（动态添加的元素，我们只能用事件委托的形式绑定）
      // 通过事件委托的方式监听文件类型input的change事件
      // 事件委托的原理就是事件冒泡，给父元素绑定事件，子元素通过事件冒泡的形式触发
      // on方法：
      //   第一个参数是事件类型
      //   第二个参数是指定需要触发该事件的子元素（包括子孙后代）选择器
      //   第三个参数是事件处理程序
      $formBox.on('change', '#touxiang', ({ target }) => {
        // console.log(target.files[0]);
        const fd = new FormData()
        // 这个键名，决定了响应数据中图片地址的键名
        fd.append('touxiang', target.files[0])

        $.ajax({
          type: 'POST',
          url: '/upload',
          // 默认情况下，jq的ajax方法会将data数据处理为urlencoded的格式
          data: fd,
          // 是否要处理data，默认true，由于我们在这里传递的是一个FormData对象数据，所以不需要处理
          processData: false,
          // 默认是urlencoded的内容类型，由于我们传递的是一个FormData对象，这个对象中自带了内容类型，所以我们不需要让jq的ajax方法指定了
          contentType: false,
          success(res) {
            // console.log(res[0].touxiang);
            const url = res[0].touxiang
            $('#previewImg').attr('src', url)
            $('#hiddenAvatar').val(url)
          }
        })

      })

      // 监听用户编辑按钮点击事件，在这里，仅仅只是把用户现有的信息，填充到左侧表单中，并不是真正的提交修改用户
      $userListBox.on('click', '.edit', ({ target }) => {
        // 根据id去现有的用户数组中找到该用户对象
        const user = userAry.find(v => v._id === target.dataset.id)
        // console.log(user);
        renderEditUserForm(user)
      })

      // 监听用户删除按钮点击事件
      $userListBox.on('click', '.del', ({ target }) => {
        const id = target.dataset.id
        // 根据用户id在数组中找到这个用户
        const user = userAry.find(v => v._id === id)

        if (confirm(`你确定要删除 "${user.nickName}" 吗？`)) {
          $.ajax({
            type: 'DELETE',
            url: `/users/${id}`,
            success() {
              // 过滤出未删除的用户数组
              userAry = userAry.filter(v => v._id !== id)
              // 重新渲染用户列表
              renderUserList()
            }
          })
        }

      })

      // 通过事件委托的方式监听添加用户表单的提交事件
      $formBox.on('submit', '#addUserForm', ({ target }) => {
        // 会去表单中检索带有name属性的表单域，自动组合成urlencoded格式的请求参数
        const data = $(target).serialize()
        // console.log(data, 'data');

        // 表单验证

        $.ajax({
          type: 'post',
          url: '/users',
          data,
          success(user) {
            // 数组API：
            // shift：在前面删除元素
            // unshift：在前面追加元素
            // pop：在后面删除元素
            // push：在后面追加元素
            userAry.unshift(user)
            renderUserList()
            renderAddUserForm()
          },
          error() {
            alert('添加失败，请稍后重试')
          }
        })

        // 阻止表单默认跳转行为
        return false
      })

      // 通过事件委托的方式监听编辑用户表单的提交事件
      $formBox.on('submit', '#editUserForm', ({ target }) => {
        const data = $(target).serialize()

        $.ajax({
          type: 'PUT',
          url: `/users/${target.dataset.id}`,
          data,
          success(newUser) {
            // // 先从已有用户数组中找出旧的用户对象
            // const oldUser = userAry.find(v => v._id === newUser._id)
            // // 用新的用户对象覆盖旧的用户对象中的属性
            // Object.assign(oldUser, newUser)



            // 找出该用户在数组中的索引
            const index = userAry.findIndex(v => v._id === newUser._id)
            // splice，一般用来删除数组元素，是一个变异方法，在这里是将新用户对象替换旧用户对象，保证数组中的位置不变
            //    第一个参数：是删除的起始下标
            //    第二个参数：是要删除的元素个数
            //    第三个和之后的N个参数：都是将要从起始下标位置开始，添加到数组中的元素
            //    返回值：是被删除的元素数组，删一个返回的也是数组
            //    变异方法：调用完后，会影响原数组的方法，比如：push、pop、splice、reverse....
            //    非变异方法：调用完后，不会影响原数组的方法，比如：forEach、filter、map、slice....
            userAry.splice(index, 1, newUser)

            // 通过上述做法的目的，是为了不让在修改完之后，用户在数组中的位置发生变化

            // 渲染用户列表
            renderUserList()
            // 渲染添加用户表单
            renderAddUserForm()
          }
        })

        // 阻止表单默认行为
        return false
      })

      // 点击编辑表单下的重置按钮时
      $formBox.on('click', '#editUserForm .reset', ({ target }) => {
        const user = userAry.find(v => v._id === target.dataset.id)
        renderEditUserForm(user)
      })

      $formBox.on('click', '#editUserForm .cancel', () => {
        renderAddUserForm()
      })

      $checkAll.change(({ target }) => {
        // 当前全选按钮的选中状态
        const checked = $checkAll.prop('checked')
        // prop和attr的区别
        //   prop是用来获取和设置指定DOM元素所对应的JS对象上的属性
        //   attr是用来获取和设置指定DOM元素所对应的HTML上的属性
        //   由于prop操作的是JS对象，所以prop可以设置任意数据类型
        //   而attr只能设置字符串，如果不是字符串，内部会调用toString方法转换成字符串
        $('#userListBox :checkbox').prop('checked', checked)

        // 如果全选按钮是选中的，那么批量删除按钮就启用（不禁用）
        // 如果全选按钮不是选中的，那么批量删除按钮就禁用
        // 这存在一个隐藏的BUG（如果列表中一条数据都木有，即使你把全选按钮选中了，批量删除按钮也不应该启用）
        $batchDelBtn.attr('disabled', !checked)

        // 补充说明：
        //  1) 由于我们这个批量删除按钮是一个 a 标签，a 标签对应的JS对象没有disabled属性，所以用 .prop 无效，只能用 .attr
        //  2) 如果把 a 标签换成 <input type="button" value="批量删除"> ，就可以使用 .prop 方法控制 disabled 属性了
        //  3) 用jq控制a标签disabled属性
        //     禁用：
        //         *.attr('disabled', true)    （推荐）
        //         *.attr('disabled', 'disabled')
        //         *.attr('disabled', '')
        //     去掉禁用：
        //         *.attr('disabled', false)   （推荐）
        //         *.removeAttr('disabled')

      })

      $userListBox.on('change', ':checkbox', () => {
        // 当前选中的用户个数
        const checkedLen = $('#userListBox :checkbox:checked').length
        // 如果所有的用户都选中了，那么全选按钮就选中，否则全选按钮就不应该选中
        // 判断？  当前用户数量  和  选中的用户数量是否相等   
        $checkAll.prop('checked', userAry.length === checkedLen)

        // 如果当前选中的用户个数大于0，那么批量删除按钮应该启用
        // 如果当前选中的用户个数等于0，那么批量删除按钮应该禁用
        $batchDelBtn.attr('disabled', checkedLen === 0)
      })

      $batchDelBtn.click(() => {
        // 如果按钮是禁用的，就直接终止继续执行
        // 做这个判断是因为禁用也能触发点击事件，因为它是一个a标签，如果是一个input或者button标签，禁用后就不会触发
        if ($batchDelBtn.attr('disabled') !== undefined) return

        var idAry = []
        // 循环把所有选中的用户的id放到idAry数组中
        $('#userListBox :checkbox:checked').each((i, v) => {
          idAry.push(v.dataset.id)
        })
        // console.log(idAry, 'idAry');
        // console.log(idAry.join('-'), 'is params');

        if (!confirm(`你确定要删除这 ${idAry.length} 个用户吗？`)) return

        $.ajax({
          type: 'DELETE',
          url: `/users/${idAry.join('-')}`,
          success() {
            // 过滤出未被删除的用户数组
            userAry = userAry.filter(v => !idAry.includes(v._id))
            renderUserList()
          }
        })

      })

    })
  </script>
</body>

</html>